Let us set some global options for all code chunks in this
document.
# Set seed for reproducibility
set.seed(1982)
# Set global options for all code chunks
knitr::opts_chunk$set(
# Disable messages printed by R code chunks
message = TRUE,
# Disable warnings printed by R code chunks
warning = TRUE,
# Show R code within code chunks in output
echo = TRUE,
# Include both R code and its results in output
include = TRUE,
# Evaluate R code chunks
eval = TRUE,
# Enable caching of R code chunks for faster rendering
cache = FALSE,
# Align figures in the center of the output
fig.align = "center",
# Enable retina display for high-resolution figures
retina = 2,
# Show errors in the output instead of stopping rendering
error = TRUE,
# Do not collapse code and output into a single block
collapse = FALSE
)
# Start the figure counter
fig_count <- 0
# Define the captioner function
captioner <- function(caption) {
fig_count <<- fig_count + 1
paste0("Figure ", fig_count, ": ", caption)
}
# Define the function to truncate a number to two decimal places
truncate_to_two <- function(x) {
floor(x * 100) / 100
}
# install.packages("INLA",repos=c(getOption("repos"),INLA="https://inla.r-inla-download.org/R/testing"), dep=TRUE)
# inla.upgrade(testing = TRUE)
# remotes::install_github("inlabru-org/inlabru", ref = "devel")
# remotes::install_github("davidbolin/rspde", ref = "devel")
# remotes::install_github("davidbolin/metricgraph", ref = "devel")
library(INLA)
## Loading required package: Matrix
## Warning in file(con, "r"): URL 'https://inla.r-inla-download.org/VERSIONS':
## Timeout of 2 seconds was reached
## This is INLA_25.04.16 built 2025-04-16 08:05:23 UTC.
## - See www.r-inla.org/contact-us for how to get help.
## - List available models/likelihoods/etc with inla.list.models()
## - Use inla.doc(<NAME>) to access documentation
## Loading required package: fmesher
## This is rSPDE 2.5.1
## - See https://davidbolin.github.io/rSPDE for vignettes and manuals.
## This is MetricGraph 1.4.1
## - See https://davidbolin.github.io/MetricGraph for vignettes and manuals.
##
## Attaching package: 'MetricGraph'
## The following object is masked from 'package:stats':
##
## filter
library(grateful)
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
We want to solve the fractional diffusion equation \[\begin{equation}
\label{eq:maineq}
\partial_t u+(\kappa^2-\Delta_\Gamma)^{\frac{\alpha}{2}} u=f \text {
on } \Gamma \times(0, T), \quad u(0)=u_0 \text { on } \Gamma,
\end{equation}\] where \(u\)
satisfies the Kirchhoff vertex conditions \[\begin{equation}
\label{eq:Kcond}
\left\{\phi\in C(\Gamma)\;\Big|\; \forall v\in V:
\sum_{e\in\mathcal{E}_v}\partial_e \phi(v)=0 \right\}
\end{equation}\]
If \(f=0\), then the solution is
given by \[\begin{equation}
\label{eq:sol_reprentation}
u(s,t) =
\displaystyle\sum_{j\in\mathbb{N}}e^{-\lambda^{\frac{\alpha}{2}}_jt}\left(u_0,
e_j\right)_{L_2(\Gamma)}e_j(s).
\end{equation}\]
# Function to build a tadpole graph and create a mesh
gets_graph_tadpole <- function(h){
edge1 <- rbind(c(0,0),c(1,0))
theta <- seq(from=-pi,to=pi,length.out = 100)
edge2 <- cbind(1+1/pi+cos(theta)/pi,sin(theta)/pi)
edges = list(edge1, edge2)
graph <- metric_graph$new(edges = edges)
graph$build_mesh(h = h)
return(graph)
}
Let \(\Gamma_T =
(\mathcal{V},\mathcal{E})\) characterize the tadpole graph with
\(\mathcal{V}= \{v_1,v_2\}\) and \(\mathcal{E}= \{e_1,e_2\}\) as specified in
Figure \(\ref{Interval.Circle.Tadpole}\)c. The left
edge \(e_1\) has length 1 and the
circular edge \(e_2\) has length 2. As
discussed in Subsection \(\ref{subsec:prelim}\), a point on \(e_1\) is parameterized via \(s=\left(e_1, t\right)\) for \(t \in[0,1]\) and a point on \(e_2\) via \(s=\left(e_2, t\right)\) for \(t\in[0,2]\). One can verify that \(-\Delta_\Gamma\) has eigenvalues \(0,\left\{(i \pi / 2)^2\right\}_{i \in
\mathbb{N}}\) and \(\left\{(i \pi /
2)^2\right\}_{2 i \in \mathbb{N}}\) with corresponding
eigenfunctions \(\phi_0\), \(\left\{\phi_i\right\}_{i \in \mathbb{N}}\),
and \(\left\{\psi_i\right\}_{2 i \in
\mathbb{N}}\) given by \(\phi_0(s)=1 /
\sqrt{3}\) and \[\begin{equation*}
\phi_i(s)=C_{\phi, i}\begin{cases}
-2 \sin (\frac{i\pi}{2}) \cos (\frac{i \pi t}{2}), & s \in
e_1, \\
\sin (i \pi t / 2), & s \in e_2,
\end{cases},
\quad
\psi_i(s)=\frac{\sqrt{3}}{\sqrt{2}} \begin{cases}
(-1)^{i / 2} \cos (\frac{i \pi t}{2}), & s \in e_1, \\
\cos (\frac{i \pi t}{2}), & s \in e_2,
\end{cases},
\end{equation*}\] where \(C_{\phi,
i}=1\) if \(i\) is even and
\(C_{\phi, i}=1 / \sqrt{3}\) otherwise.
Moreover, these functions form an orthonormal basis for \(L_2(\Gamma_T)\).
# Function to compute the eigenfunctions
tadpole.eig <- function(k,graph){
x1 <- c(0,graph$get_edge_lengths()[1]*graph$mesh$PtE[graph$mesh$PtE[,1]==1,2])
x2 <- c(0,graph$get_edge_lengths()[2]*graph$mesh$PtE[graph$mesh$PtE[,1]==2,2])
if(k==0){
f.e1 <- rep(1,length(x1))
f.e2 <- rep(1,length(x2))
f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1])
f = list(phi=f1/sqrt(3))
} else {
f.e1 <- -2*sin(pi*k*1/2)*cos(pi*k*x1/2)
f.e2 <- sin(pi*k*x2/2)
f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1])
if((k %% 2)==1){
f = list(phi=f1/sqrt(3))
} else {
f.e1 <- (-1)^{k/2}*cos(pi*k*x1/2)
f.e2 <- cos(pi*k*x2/2)
f2 = c(f.e1[1],f.e2[1],f.e1[-1],f.e2[-1])
f <- list(phi=f1,psi=f2/sqrt(3/2))
}
}
return(f)
}
Implementation of \(u\)
h <- 0.01
graph <- gets_graph_tadpole(h = h)
## Starting graph creation...
## LongLat is set to FALSE
## Creating edges...
## Setting edge weights...
## Computing bounding box...
## Setting up edges
## Merging close vertices
## Total construction time: 0.15 secs
## Creating and updating vertices...
## Storing the initial graph...
## Computing the relative positions of the edges...
T_final <- 2
time_step <- 0.01
time_seq <- seq(0, T_final, by = time_step)
# Compute the FEM matrices
graph$compute_fem()
G <- graph$mesh$G
C <- graph$mesh$C
I <- Matrix::Diagonal(nrow(C))
x <- graph$mesh$V[, 1]
y <- graph$mesh$V[, 2]
edge_number <- graph$mesh$VtE[, 1]
pos <- sum(edge_number == 1)+1
order_to_plot <- function(v)return(c(v[1], v[3:pos], v[2], v[(pos+1):length(v)], v[2]))
weights <- graph$mesh$weights
# Initial condition
U_0 <- 10*exp(-((x-1)^2 + (y)^2))
U_true <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_true[, 1] <- U_0
kappa <- 1
alpha <- 6
beta <- alpha/2
L <- kappa^2*C + G
op <- fractional.operators(L, beta, C, scale.factor = kappa^2, m = 4)
Pl <- op$Pl
Pr <- op$Pr
Ci <- op$Ci
n_finite <- 1000
for (k in 1:(length(time_seq) - 1)) {
aux_k <- rep(0, nrow(C))
for (j in 0:n_finite) {
decay_j <- exp(-time_seq[k+1]*(kappa^2 + (j*pi/2)^2)^(alpha/2))
e_j <- tadpole.eig(j,graph)$phi
aux_k <- aux_k + decay_j*sum(U_0*e_j*weights)*e_j
if (j>0 && (j %% 2 == 0)) {
e_j <- tadpole.eig(j,graph)$psi
aux_k <- aux_k + decay_j*sum(U_0*e_j*weights)*e_j
}
}
U_true[, k + 1] <- aux_k
}
Pr.apply.mult <- function(v){return(Pr.mult(op, v))}
Pl.apply.solve <- function(v){return(Pl.solve(op, v))}
PliC <- apply(C, 2, Pl.apply.solve) # PlC^-1
# Precompute the LHS1 matrix
LHS1 <- Pr + time_step * solve(C, Pl)
# Precompute the LHS2 matrix
aux <- apply(PliC, 2, Pr.apply.mult) #PrPl^-1C
LHS2 <- aux + time_step * Matrix::Diagonal(nrow(C))
# Initialize U matrix to store solution at each time step
U_approx1 <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx1[, 1] <- U_0
U_approx2 <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx2[, 1] <- U_0
# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
# Compute the right-hand side for the first equation
RHS1 <- Pr.mult(op, U_approx1[, k])
U_approx1[, k + 1] <- as.matrix(solve(LHS1, RHS1))
# Compute the right-hand side for the second equation
RHS2 <- aux %*% U_approx2[, k]
U_approx2[, k + 1] <- as.matrix(solve(LHS2, RHS2))
}
x <- order_to_plot(x)
y <- order_to_plot(y)
max_error_at_each_time1 <- apply(abs(U_true - U_approx1), 2, max)
max_error_at_each_time2 <- apply(abs(U_true - U_approx2), 2, max)
max_error_between_both_approx <- apply(abs(U_approx1 - U_approx2), 2, max)
U_true <- apply(U_true, 2, order_to_plot)
U_approx1 <- apply(U_approx1, 2, order_to_plot)
U_approx2 <- apply(U_approx2, 2, order_to_plot)
# Create interactive plot
fig <- plot_ly()
# Add first line (max_error_at_each_time1)
fig <- fig %>% add_trace(
x = ~time_seq, y = ~max_error_at_each_time1, type = 'scatter', mode = 'lines+markers',
line = list(color = 'red', width = 2),
marker = list(size = 4),
name = "Max Error True and Approx 1"
)
# Add second line (max_error_at_each_time2)
fig <- fig %>% add_trace(
x = ~time_seq, y = ~max_error_at_each_time2, type = 'scatter', mode = 'lines+markers',
line = list(color = 'blue', width = 2, dash = 'dash'),
marker = list(size = 4),
name = "Max Error True and Approx 2"
)
# Add third line (max_error_between_both_approx)
fig <- fig %>% add_trace(
x = ~time_seq, y = ~max_error_between_both_approx, type = 'scatter', mode = 'lines+markers',
line = list(color = 'green', width = 2, dash = 'dot'),
marker = list(size = 4),
name = "Max Error Between Approximations"
)
# Layout
fig <- fig %>% layout(
title = "Max Error at Each Time Step",
xaxis = list(title = "Time"),
yaxis = list(title = "Max Error"),
legend = list(x = 0.1, y = 0.9)
)
plot_data <- data.frame(
x = rep(x, times = ncol(U_true)),
y = rep(y, times = ncol(U_true)),
z_true = as.vector(U_true),
z_approx1 = as.vector(U_approx1),
z_approx2 = as.vector(U_approx2),
frame = rep(time_seq, each = length(x))
)
# Compute axis limits
x_range <- range(x)
y_range <- range(y)
z_range <- range(c(U_true, U_approx1, U_approx2))
# Initial plot setup (first frame only)
p <- plot_ly(plot_data, frame = ~frame) %>%
add_trace(
x = ~x, y = ~y, z = ~z_true,
type = "scatter3d", mode = "lines",
name = "True",
line = list(color = "blue", width = 2)
) %>%
add_trace(
x = ~x, y = ~y, z = ~z_approx1,
type = "scatter3d", mode = "lines",
name = "Approx 1",
line = list(color = "red", width = 2)
) %>%
add_trace(
x = ~x, y = ~y, z = ~z_approx2,
type = "scatter3d", mode = "lines",
name = "Approx 2",
line = list(color = "green", width = 2)
) %>%
layout(
scene = list(
xaxis = list(title = "x", range = x_range),
yaxis = list(title = "y", range = y_range),
zaxis = list(title = "Value", range = z_range)
),
updatemenus = list(
list(
type = "buttons", showactive = FALSE,
buttons = list(
list(label = "Play", method = "animate",
args = list(NULL, list(frame = list(duration = 100, redraw = TRUE), fromcurrent = TRUE))),
list(label = "Pause", method = "animate",
args = list(NULL, list(mode = "immediate", frame = list(duration = 0), redraw = FALSE)))
)
)
),
title = "Time: 0"
)
# Convert to plotly object with frame info
pb <- plotly_build(p)
# Inject custom titles into each frame
for (i in seq_along(pb$x$frames)) {
t <- time_seq[i]
err <- signif(max_error_at_each_time2[i], 4)
pb$x$frames[[i]]$layout <- list(title = paste0("Time: ", t, " | Max Error: ", err))
}
LS0tCnRpdGxlOiAiU29sdmluZyBhIHBhcmFib2xpYyBlcXVhdGlvbiIKZGF0ZTogIkNyZWF0ZWQ6IDIwLTA0LTIwMjUuIExhc3QgbW9kaWZpZWQ6IGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVkuJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIG1hdGhqYXg6ICJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL21hdGhqYXhAMy9lczUvdGV4LW1tbC1jaHRtbC5qcyIKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBmbGF0bHkKICAgIGNvZGVfZm9sZGluZzogc2hvdyAjIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiIHRvIGhpZGUgY29kZSBhbmQgYWRkIGEgYnV0dG9uIHRvIHNob3cgaXQKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQpiaWJsaW9ncmFwaHk6IAogIC0gcmVmZXJlbmNlcy5iaWIKICAtIGdyYXRlZnVsLXJlZnMuYmliCmhlYWRlci1pbmNsdWRlczoKICAtIFxuZXdjb21tYW5ke1xhcn17XG1hdGhiYntSfX0KICAtIFxuZXdjb21tYW5ke1xsbGF2fVsxXXtcbGVmdFx7IzFccmlnaHRcfX0KICAtIFxuZXdjb21tYW5ke1xwYXJlfVsxXXtcbGVmdCgjMVxyaWdodCl9CiAgLSBcbmV3Y29tbWFuZHtcTmNhbH17XG1hdGhjYWx7Tn19CiAgLSBcbmV3Y29tbWFuZHtcVmNhbH17XG1hdGhjYWx7Vn19CiAgLSBcbmV3Y29tbWFuZHtcRWNhbH17XG1hdGhjYWx7RX19CiAgLSBcbmV3Y29tbWFuZHtcV2NhbH17XG1hdGhjYWx7V319Ci0tLQoKYGBge3IgeGFyaW5nYW5FeHRyYS1jbGlwYm9hcmQsIGVjaG8gPSBGQUxTRX0KaHRtbHRvb2xzOjp0YWdMaXN0KAogIHhhcmluZ2FuRXh0cmE6OnVzZV9jbGlwYm9hcmQoCiAgICBidXR0b25fdGV4dCA9ICI8aSBjbGFzcz1cImZhLXNvbGlkIGZhLWNsaXBib2FyZFwiIHN0eWxlPVwiY29sb3I6ICMwMDAwOEJcIj48L2k+IiwKICAgIHN1Y2Nlc3NfdGV4dCA9ICI8aSBjbGFzcz1cImZhIGZhLWNoZWNrXCIgc3R5bGU9XCJjb2xvcjogIzkwQkU2RFwiPjwvaT4iLAogICAgZXJyb3JfdGV4dCA9ICI8aSBjbGFzcz1cImZhIGZhLXRpbWVzLWNpcmNsZVwiIHN0eWxlPVwiY29sb3I6ICNGOTQxNDRcIj48L2k+IgogICksCiAgcm1hcmtkb3duOjpodG1sX2RlcGVuZGVuY3lfZm9udF9hd2Vzb21lKCkKKQpgYGAKCgpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9CmJvZHkgLm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDEwMCUgIWltcG9ydGFudDsKICB3aWR0aDogMTAwJSAhaW1wb3J0YW50Owp9CmJvZHkgewogIG1heC13aWR0aDogMTAwJSAhaW1wb3J0YW50Owp9Cgpib2R5LCB0ZCB7CiAgIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxNHB4Owp9CnByZSB7CiAgZm9udC1zaXplOiAxNHB4Cn0KLmN1c3RvbS1ib3ggewogIGJhY2tncm91bmQtY29sb3I6ICNmNWY3ZmE7IC8qIExpZ2h0IGdyZXktYmx1ZSBiYWNrZ3JvdW5kICovCiAgYm9yZGVyLWNvbG9yOiAjZTFlOGVkOyAvKiBMaWdodCBib3JkZXIgY29sb3IgKi8KICBjb2xvcjogIzJjM2U1MDsgLyogRGFyayB0ZXh0IGNvbG9yICovCiAgcGFkZGluZzogMTVweDsgLyogUGFkZGluZyBpbnNpZGUgdGhlIGJveCAqLwogIGJvcmRlci1yYWRpdXM6IDVweDsgLyogUm91bmRlZCBjb3JuZXJzICovCiAgbWFyZ2luLWJvdHRvbTogMjBweDsgLyogU3BhY2luZyBiZWxvdyB0aGUgYm94ICovCn0KLmNhcHRpb24gewogIG1hcmdpbjogYXV0bzsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgbWFyZ2luLWJvdHRvbTogMjBweDsgLyogU3BhY2luZyBiZWxvdyB0aGUgYm94ICovCn0KYGBgCgoKTGV0IHVzIHNldCBzb21lIGdsb2JhbCBvcHRpb25zIGZvciBhbGwgY29kZSBjaHVua3MgaW4gdGhpcyBkb2N1bWVudC4KCgpgYGB7cn0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDE5ODIpIAojIFNldCBnbG9iYWwgb3B0aW9ucyBmb3IgYWxsIGNvZGUgY2h1bmtzCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAjIERpc2FibGUgbWVzc2FnZXMgcHJpbnRlZCBieSBSIGNvZGUgY2h1bmtzCiAgbWVzc2FnZSA9IFRSVUUsICAgIAogICMgRGlzYWJsZSB3YXJuaW5ncyBwcmludGVkIGJ5IFIgY29kZSBjaHVua3MKICB3YXJuaW5nID0gVFJVRSwgICAgCiAgIyBTaG93IFIgY29kZSB3aXRoaW4gY29kZSBjaHVua3MgaW4gb3V0cHV0CiAgZWNobyA9IFRSVUUsICAgICAgICAKICAjIEluY2x1ZGUgYm90aCBSIGNvZGUgYW5kIGl0cyByZXN1bHRzIGluIG91dHB1dAogIGluY2x1ZGUgPSBUUlVFLCAgICAgCiAgIyBFdmFsdWF0ZSBSIGNvZGUgY2h1bmtzCiAgZXZhbCA9IFRSVUUsICAgICAgIAogICMgRW5hYmxlIGNhY2hpbmcgb2YgUiBjb2RlIGNodW5rcyBmb3IgZmFzdGVyIHJlbmRlcmluZwogIGNhY2hlID0gRkFMU0UsICAgICAgCiAgIyBBbGlnbiBmaWd1cmVzIGluIHRoZSBjZW50ZXIgb2YgdGhlIG91dHB1dAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogICMgRW5hYmxlIHJldGluYSBkaXNwbGF5IGZvciBoaWdoLXJlc29sdXRpb24gZmlndXJlcwogIHJldGluYSA9IDIsCiAgIyBTaG93IGVycm9ycyBpbiB0aGUgb3V0cHV0IGluc3RlYWQgb2Ygc3RvcHBpbmcgcmVuZGVyaW5nCiAgZXJyb3IgPSBUUlVFLAogICMgRG8gbm90IGNvbGxhcHNlIGNvZGUgYW5kIG91dHB1dCBpbnRvIGEgc2luZ2xlIGJsb2NrCiAgY29sbGFwc2UgPSBGQUxTRQopCiMgU3RhcnQgdGhlIGZpZ3VyZSBjb3VudGVyCmZpZ19jb3VudCA8LSAwCiMgRGVmaW5lIHRoZSBjYXB0aW9uZXIgZnVuY3Rpb24KY2FwdGlvbmVyIDwtIGZ1bmN0aW9uKGNhcHRpb24pIHsKICBmaWdfY291bnQgPDwtIGZpZ19jb3VudCArIDEKICBwYXN0ZTAoIkZpZ3VyZSAiLCBmaWdfY291bnQsICI6ICIsIGNhcHRpb24pCn0KIyBEZWZpbmUgdGhlIGZ1bmN0aW9uIHRvIHRydW5jYXRlIGEgbnVtYmVyIHRvIHR3byBkZWNpbWFsIHBsYWNlcwp0cnVuY2F0ZV90b190d28gPC0gZnVuY3Rpb24oeCkgewogIGZsb29yKHggKiAxMDApIC8gMTAwCn0KYGBgCgoKCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJJTkxBIixyZXBvcz1jKGdldE9wdGlvbigicmVwb3MiKSxJTkxBPSJodHRwczovL2lubGEuci1pbmxhLWRvd25sb2FkLm9yZy9SL3Rlc3RpbmciKSwgZGVwPVRSVUUpCiMgaW5sYS51cGdyYWRlKHRlc3RpbmcgPSBUUlVFKQojIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJpbmxhYnJ1LW9yZy9pbmxhYnJ1IiwgcmVmID0gImRldmVsIikKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGF2aWRib2xpbi9yc3BkZSIsIHJlZiA9ICJkZXZlbCIpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImRhdmlkYm9saW4vbWV0cmljZ3JhcGgiLCByZWYgPSAiZGV2ZWwiKQpsaWJyYXJ5KElOTEEpCmxpYnJhcnkoaW5sYWJydSkKbGlicmFyeShyU1BERSkKbGlicmFyeShNZXRyaWNHcmFwaCkKbGlicmFyeShncmF0ZWZ1bCkKCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpXZSB3YW50IHRvIHNvbHZlIHRoZSBmcmFjdGlvbmFsIGRpZmZ1c2lvbiBlcXVhdGlvbgpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTptYWluZXF9CiAgICBccGFydGlhbF90IHUrKFxrYXBwYV4yLVxEZWx0YV9cR2FtbWEpXntcZnJhY3tcYWxwaGF9ezJ9fSB1PWYgXHRleHQgeyBvbiB9IFxHYW1tYSBcdGltZXMoMCwgVCksIFxxdWFkIHUoMCk9dV8wIFx0ZXh0IHsgb24gfSBcR2FtbWEsClxlbmR7ZXF1YXRpb259CndoZXJlICR1JCBzYXRpc2ZpZXMgdGhlIEtpcmNoaG9mZiB2ZXJ0ZXggY29uZGl0aW9ucwpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTpLY29uZH0KICAgIFxsZWZ0XHtccGhpXGluIEMoXEdhbW1hKVw7XEJpZ3xcOyBcZm9yYWxsIHZcaW4gVjogXHN1bV97ZVxpblxtYXRoY2Fse0V9X3Z9XHBhcnRpYWxfZSBccGhpKHYpPTAgXHJpZ2h0XH0KXGVuZHtlcXVhdGlvbn0KCklmICRmPTAkLCB0aGVuIHRoZSBzb2x1dGlvbiBpcyBnaXZlbiBieQpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTpzb2xfcmVwcmVudGF0aW9ufQogICAgICAgIHUocyx0KSA9IFxkaXNwbGF5c3R5bGVcc3VtX3tqXGluXG1hdGhiYntOfX1lXnstXGxhbWJkYV57XGZyYWN7XGFscGhhfXsyfX1fanR9XGxlZnQodV8wLCBlX2pccmlnaHQpX3tMXzIoXEdhbW1hKX1lX2oocykuClxlbmR7ZXF1YXRpb259CgpgYGB7cn0KIyBGdW5jdGlvbiB0byBidWlsZCBhIHRhZHBvbGUgZ3JhcGggYW5kIGNyZWF0ZSBhIG1lc2gKZ2V0c19ncmFwaF90YWRwb2xlIDwtIGZ1bmN0aW9uKGgpewogIGVkZ2UxIDwtIHJiaW5kKGMoMCwwKSxjKDEsMCkpCiAgdGhldGEgPC0gc2VxKGZyb209LXBpLHRvPXBpLGxlbmd0aC5vdXQgPSAxMDApCiAgZWRnZTIgPC0gY2JpbmQoMSsxL3BpK2Nvcyh0aGV0YSkvcGksc2luKHRoZXRhKS9waSkKICBlZGdlcyA9IGxpc3QoZWRnZTEsIGVkZ2UyKQogIGdyYXBoIDwtIG1ldHJpY19ncmFwaCRuZXcoZWRnZXMgPSBlZGdlcykKICBncmFwaCRidWlsZF9tZXNoKGggPSBoKQogIHJldHVybihncmFwaCkKfQpgYGAKCkxldCAkXEdhbW1hX1QgPSAoXFZjYWwsXEVjYWwpJCBjaGFyYWN0ZXJpemUgdGhlIHRhZHBvbGUgZ3JhcGggd2l0aCAkXFZjYWwgPSBce3ZfMSx2XzJcfSQgYW5kICRcRWNhbCA9IFx7ZV8xLGVfMlx9JCBhcyBzcGVjaWZpZWQgaW4gRmlndXJlIFxyZWZ7SW50ZXJ2YWwuQ2lyY2xlLlRhZHBvbGV9Yy4gVGhlIGxlZnQgZWRnZSAkZV8xJCBoYXMgbGVuZ3RoIDEgYW5kIHRoZSBjaXJjdWxhciBlZGdlICRlXzIkIGhhcyBsZW5ndGggMi4gQXMgZGlzY3Vzc2VkIGluIFN1YnNlY3Rpb24gXHJlZntzdWJzZWM6cHJlbGltfSwgYSBwb2ludCBvbiAkZV8xJCBpcyBwYXJhbWV0ZXJpemVkIHZpYSAkcz1cbGVmdChlXzEsIHRccmlnaHQpJCBmb3IgJHQgXGluWzAsMV0kIGFuZCBhIHBvaW50IG9uICRlXzIkIHZpYSAkcz1cbGVmdChlXzIsIHRccmlnaHQpJCBmb3IgJHRcaW5bMCwyXSQuIE9uZSBjYW4gdmVyaWZ5IHRoYXQgJC1cRGVsdGFfXEdhbW1hJCBoYXMgZWlnZW52YWx1ZXMgJDAsXGxlZnRceyhpIFxwaSAvIDIpXjJccmlnaHRcfV97aSBcaW4gXG1hdGhiYntOfX0kIGFuZCAkXGxlZnRceyhpIFxwaSAvIDIpXjJccmlnaHRcfV97MiBpIFxpbiBcbWF0aGJie059fSQgd2l0aCBjb3JyZXNwb25kaW5nIGVpZ2VuZnVuY3Rpb25zICRccGhpXzAkLCAkXGxlZnRce1xwaGlfaVxyaWdodFx9X3tpIFxpbiBcbWF0aGJie059fSQsIGFuZCAkXGxlZnRce1xwc2lfaVxyaWdodFx9X3syIGkgXGluIFxtYXRoYmJ7Tn19JCBnaXZlbiBieSAkXHBoaV8wKHMpPTEgLyBcc3FydHszfSQgYW5kIApcYmVnaW57ZXF1YXRpb24qfQogICAgXHBoaV9pKHMpPUNfe1xwaGksIGl9XGJlZ2lue2Nhc2VzfQogICAgICAgIC0yIFxzaW4gKFxmcmFje2lccGl9ezJ9KSBcY29zIChcZnJhY3tpIFxwaSB0fXsyfSksICYgcyBcaW4gZV8xLCBcXApcc2luIChpIFxwaSB0IC8gMiksICYgcyBcaW4gZV8yLAogICAgXGVuZHtjYXNlc30sClxxdWFkIAogICAgXHBzaV9pKHMpPVxmcmFje1xzcXJ0ezN9fXtcc3FydHsyfX0gXGJlZ2lue2Nhc2VzfQogICAgKC0xKV57aSAvIDJ9IFxjb3MgKFxmcmFje2kgXHBpIHR9ezJ9KSwgJiBzIFxpbiBlXzEsIFxcClxjb3MgKFxmcmFje2kgXHBpIHR9ezJ9KSwgJiBzIFxpbiBlXzIsClxlbmR7Y2FzZXN9LApcZW5ke2VxdWF0aW9uKn0Kd2hlcmUgJENfe1xwaGksIGl9PTEkIGlmICRpJCBpcyBldmVuIGFuZCAkQ197XHBoaSwgaX09MSAvIFxzcXJ0ezN9JCBvdGhlcndpc2UuIE1vcmVvdmVyLCB0aGVzZSBmdW5jdGlvbnMgZm9ybSBhbiBvcnRob25vcm1hbCBiYXNpcyBmb3IgJExfMihcR2FtbWFfVCkkLgoKYGBge3J9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgZWlnZW5mdW5jdGlvbnMgCnRhZHBvbGUuZWlnIDwtIGZ1bmN0aW9uKGssZ3JhcGgpewp4MSA8LSBjKDAsZ3JhcGgkZ2V0X2VkZ2VfbGVuZ3RocygpWzFdKmdyYXBoJG1lc2gkUHRFW2dyYXBoJG1lc2gkUHRFWywxXT09MSwyXSkgCngyIDwtIGMoMCxncmFwaCRnZXRfZWRnZV9sZW5ndGhzKClbMl0qZ3JhcGgkbWVzaCRQdEVbZ3JhcGgkbWVzaCRQdEVbLDFdPT0yLDJdKSAKCmlmKGs9PTApeyAKICBmLmUxIDwtIHJlcCgxLGxlbmd0aCh4MSkpIAogIGYuZTIgPC0gcmVwKDEsbGVuZ3RoKHgyKSkgCiAgZjEgPSBjKGYuZTFbMV0sZi5lMlsxXSxmLmUxWy0xXSwgZi5lMlstMV0pIAogIGYgPSBsaXN0KHBoaT1mMS9zcXJ0KDMpKSAKICAKfSBlbHNlIHsKICBmLmUxIDwtIC0yKnNpbihwaSprKjEvMikqY29zKHBpKmsqeDEvMikgCiAgZi5lMiA8LSBzaW4ocGkqayp4Mi8yKSAgICAgICAgICAgICAgICAgIAogIAogIGYxID0gYyhmLmUxWzFdLGYuZTJbMV0sZi5lMVstMV0sIGYuZTJbLTFdKSAKICAKICBpZigoayAlJSAyKT09MSl7IAogICAgZiA9IGxpc3QocGhpPWYxL3NxcnQoMykpIAogIH0gZWxzZSB7IAogICAgZi5lMSA8LSAoLTEpXntrLzJ9KmNvcyhwaSprKngxLzIpCiAgICBmLmUyIDwtIGNvcyhwaSprKngyLzIpCiAgICBmMiA9IGMoZi5lMVsxXSxmLmUyWzFdLGYuZTFbLTFdLGYuZTJbLTFdKSAKICAgIGYgPC0gbGlzdChwaGk9ZjEscHNpPWYyL3NxcnQoMy8yKSkKICB9Cn0KCnJldHVybihmKQp9CmBgYAoKSW1wbGVtZW50YXRpb24gb2YgJHUkCgpgYGB7cn0KaCA8LSAwLjAxCmdyYXBoIDwtIGdldHNfZ3JhcGhfdGFkcG9sZShoID0gaCkKVF9maW5hbCA8LSAyCnRpbWVfc3RlcCA8LSAwLjAxCnRpbWVfc2VxIDwtIHNlcSgwLCBUX2ZpbmFsLCBieSA9IHRpbWVfc3RlcCkKIyBDb21wdXRlIHRoZSBGRU0gbWF0cmljZXMKZ3JhcGgkY29tcHV0ZV9mZW0oKQpHIDwtIGdyYXBoJG1lc2gkRwpDIDwtIGdyYXBoJG1lc2gkQwpJIDwtIE1hdHJpeDo6RGlhZ29uYWwobnJvdyhDKSkKeCA8LSBncmFwaCRtZXNoJFZbLCAxXQp5IDwtIGdyYXBoJG1lc2gkVlssIDJdCmVkZ2VfbnVtYmVyIDwtIGdyYXBoJG1lc2gkVnRFWywgMV0KcG9zIDwtIHN1bShlZGdlX251bWJlciA9PSAxKSsxCm9yZGVyX3RvX3Bsb3QgPC0gZnVuY3Rpb24odilyZXR1cm4oYyh2WzFdLCB2WzM6cG9zXSwgdlsyXSwgdlsocG9zKzEpOmxlbmd0aCh2KV0sIHZbMl0pKQp3ZWlnaHRzIDwtIGdyYXBoJG1lc2gkd2VpZ2h0cwojIEluaXRpYWwgY29uZGl0aW9uClVfMCA8LSAxMCpleHAoLSgoeC0xKV4yICsgKHkpXjIpKQoKVV90cnVlIDwtIG1hdHJpeChOQSwgbnJvdyA9IG5yb3coQyksIG5jb2wgPSBsZW5ndGgodGltZV9zZXEpKQpVX3RydWVbLCAxXSA8LSBVXzAKYGBgCgoKYGBge3J9CmthcHBhIDwtIDEKYWxwaGEgPC0gNgpiZXRhIDwtIGFscGhhLzIKTCA8LSBrYXBwYV4yKkMgKyBHCm9wIDwtIGZyYWN0aW9uYWwub3BlcmF0b3JzKEwsIGJldGEsIEMsIHNjYWxlLmZhY3RvciA9IGthcHBhXjIsIG0gPSA0KQpQbCA8LSBvcCRQbApQciA8LSBvcCRQcgpDaSA8LSBvcCRDaQpgYGAKCgpgYGB7cn0Kbl9maW5pdGUgPC0gMTAwMAoKCmZvciAoayBpbiAxOihsZW5ndGgodGltZV9zZXEpIC0gMSkpIHsKICBhdXhfayA8LSByZXAoMCwgbnJvdyhDKSkKICBmb3IgKGogaW4gMDpuX2Zpbml0ZSkgewogICAgZGVjYXlfaiA8LSBleHAoLXRpbWVfc2VxW2srMV0qKGthcHBhXjIgKyAoaipwaS8yKV4yKV4oYWxwaGEvMikpCiAgICBlX2ogPC0gdGFkcG9sZS5laWcoaixncmFwaCkkcGhpCiAgICBhdXhfayA8LSBhdXhfayArIGRlY2F5X2oqc3VtKFVfMCplX2oqd2VpZ2h0cykqZV9qCiAgICBpZiAoaj4wICYmIChqICUlIDIgPT0gMCkpIHsKICAgICAgZV9qIDwtIHRhZHBvbGUuZWlnKGosZ3JhcGgpJHBzaQogICAgICBhdXhfayA8LSBhdXhfayArIGRlY2F5X2oqc3VtKFVfMCplX2oqd2VpZ2h0cykqZV9qCiAgICAgIH0KICAgIH0KICBVX3RydWVbLCBrICsgMV0gPC0gYXV4X2sKfQpgYGAKCmBgYHtyfQoKUHIuYXBwbHkubXVsdCA8LSBmdW5jdGlvbih2KXtyZXR1cm4oUHIubXVsdChvcCwgdikpfQpQbC5hcHBseS5zb2x2ZSA8LSBmdW5jdGlvbih2KXtyZXR1cm4oUGwuc29sdmUob3AsIHYpKX0KUGxpQyA8LSBhcHBseShDLCAyLCBQbC5hcHBseS5zb2x2ZSkgIyBQbENeLTEKIyBQcmVjb21wdXRlIHRoZSBMSFMxIG1hdHJpeApMSFMxIDwtIFByICsgdGltZV9zdGVwICogc29sdmUoQywgUGwpCiMgUHJlY29tcHV0ZSB0aGUgTEhTMiBtYXRyaXgKYXV4IDwtIGFwcGx5KFBsaUMsIDIsIFByLmFwcGx5Lm11bHQpICNQclBsXi0xQwpMSFMyIDwtIGF1eCArIHRpbWVfc3RlcCAqIE1hdHJpeDo6RGlhZ29uYWwobnJvdyhDKSkgCgojIEluaXRpYWxpemUgVSBtYXRyaXggdG8gc3RvcmUgc29sdXRpb24gYXQgZWFjaCB0aW1lIHN0ZXAKVV9hcHByb3gxIDwtIG1hdHJpeChOQSwgbnJvdyA9IG5yb3coQyksIG5jb2wgPSBsZW5ndGgodGltZV9zZXEpKQpVX2FwcHJveDFbLCAxXSA8LSBVXzAKClVfYXBwcm94MiA8LSBtYXRyaXgoTkEsIG5yb3cgPSBucm93KEMpLCBuY29sID0gbGVuZ3RoKHRpbWVfc2VxKSkKVV9hcHByb3gyWywgMV0gPC0gVV8wCgojIFRpbWUtc3RlcHBpbmcgbG9vcApmb3IgKGsgaW4gMToobGVuZ3RoKHRpbWVfc2VxKSAtIDEpKSB7CiAgIyBDb21wdXRlIHRoZSByaWdodC1oYW5kIHNpZGUgZm9yIHRoZSBmaXJzdCBlcXVhdGlvbgogIFJIUzEgPC0gUHIubXVsdChvcCwgVV9hcHByb3gxWywga10pCiAgVV9hcHByb3gxWywgayArIDFdIDwtIGFzLm1hdHJpeChzb2x2ZShMSFMxLCBSSFMxKSkKICAjIENvbXB1dGUgdGhlIHJpZ2h0LWhhbmQgc2lkZSBmb3IgdGhlIHNlY29uZCBlcXVhdGlvbgogIFJIUzIgPC0gYXV4ICUqJSBVX2FwcHJveDJbLCBrXQogIFVfYXBwcm94MlssIGsgKyAxXSA8LSBhcy5tYXRyaXgoc29sdmUoTEhTMiwgUkhTMikpCn0KYGBgCgoKCmBgYHtyfQp4IDwtIG9yZGVyX3RvX3Bsb3QoeCkKeSA8LSBvcmRlcl90b19wbG90KHkpCm1heF9lcnJvcl9hdF9lYWNoX3RpbWUxIDwtIGFwcGx5KGFicyhVX3RydWUgLSBVX2FwcHJveDEpLCAyLCBtYXgpCm1heF9lcnJvcl9hdF9lYWNoX3RpbWUyIDwtIGFwcGx5KGFicyhVX3RydWUgLSBVX2FwcHJveDIpLCAyLCBtYXgpCm1heF9lcnJvcl9iZXR3ZWVuX2JvdGhfYXBwcm94IDwtIGFwcGx5KGFicyhVX2FwcHJveDEgLSBVX2FwcHJveDIpLCAyLCBtYXgpCgpVX3RydWUgPC0gYXBwbHkoVV90cnVlLCAyLCBvcmRlcl90b19wbG90KQpVX2FwcHJveDEgPC0gYXBwbHkoVV9hcHByb3gxLCAyLCBvcmRlcl90b19wbG90KQpVX2FwcHJveDIgPC0gYXBwbHkoVV9hcHByb3gyLCAyLCBvcmRlcl90b19wbG90KQoKIyBDcmVhdGUgaW50ZXJhY3RpdmUgcGxvdApmaWcgPC0gcGxvdF9seSgpCgojIEFkZCBmaXJzdCBsaW5lIChtYXhfZXJyb3JfYXRfZWFjaF90aW1lMSkKZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKAogIHggPSB+dGltZV9zZXEsIHkgPSB+bWF4X2Vycm9yX2F0X2VhY2hfdGltZTEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsCiAgbGluZSA9IGxpc3QoY29sb3IgPSAncmVkJywgd2lkdGggPSAyKSwKICBtYXJrZXIgPSBsaXN0KHNpemUgPSA0KSwKICBuYW1lID0gIk1heCBFcnJvciAgVHJ1ZSBhbmQgQXBwcm94IDEiCikKCiMgQWRkIHNlY29uZCBsaW5lIChtYXhfZXJyb3JfYXRfZWFjaF90aW1lMikKZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKAogIHggPSB+dGltZV9zZXEsIHkgPSB+bWF4X2Vycm9yX2F0X2VhY2hfdGltZTIsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsCiAgbGluZSA9IGxpc3QoY29sb3IgPSAnYmx1ZScsIHdpZHRoID0gMiwgZGFzaCA9ICdkYXNoJyksCiAgbWFya2VyID0gbGlzdChzaXplID0gNCksCiAgbmFtZSA9ICJNYXggRXJyb3IgVHJ1ZSBhbmQgQXBwcm94IDIiCikKCiMgQWRkIHRoaXJkIGxpbmUgKG1heF9lcnJvcl9iZXR3ZWVuX2JvdGhfYXBwcm94KQoKZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKAogIHggPSB+dGltZV9zZXEsIHkgPSB+bWF4X2Vycm9yX2JldHdlZW5fYm90aF9hcHByb3gsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMrbWFya2VycycsCiAgbGluZSA9IGxpc3QoY29sb3IgPSAnZ3JlZW4nLCB3aWR0aCA9IDIsIGRhc2ggPSAnZG90JyksCiAgbWFya2VyID0gbGlzdChzaXplID0gNCksCiAgbmFtZSA9ICJNYXggRXJyb3IgQmV0d2VlbiBBcHByb3hpbWF0aW9ucyIKKQoKIyBMYXlvdXQKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KAogIHRpdGxlID0gIk1heCBFcnJvciBhdCBFYWNoIFRpbWUgU3RlcCIsCiAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlRpbWUiKSwKICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTWF4IEVycm9yIiksCiAgbGVnZW5kID0gbGlzdCh4ID0gMC4xLCB5ID0gMC45KQopCgoKcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHJlcCh4LCB0aW1lcyA9IG5jb2woVV90cnVlKSksCiAgeSA9IHJlcCh5LCB0aW1lcyA9IG5jb2woVV90cnVlKSksCiAgel90cnVlID0gYXMudmVjdG9yKFVfdHJ1ZSksCiAgel9hcHByb3gxID0gYXMudmVjdG9yKFVfYXBwcm94MSksCiAgel9hcHByb3gyID0gYXMudmVjdG9yKFVfYXBwcm94MiksCiAgZnJhbWUgPSByZXAodGltZV9zZXEsIGVhY2ggPSBsZW5ndGgoeCkpCikKCiMgQ29tcHV0ZSBheGlzIGxpbWl0cwp4X3JhbmdlIDwtIHJhbmdlKHgpCnlfcmFuZ2UgPC0gcmFuZ2UoeSkKel9yYW5nZSA8LSByYW5nZShjKFVfdHJ1ZSwgVV9hcHByb3gxLCBVX2FwcHJveDIpKQoKIyBJbml0aWFsIHBsb3Qgc2V0dXAgKGZpcnN0IGZyYW1lIG9ubHkpCnAgPC0gcGxvdF9seShwbG90X2RhdGEsIGZyYW1lID0gfmZyYW1lKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56X3RydWUsCiAgICB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLAogICAgbmFtZSA9ICJUcnVlIiwKICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiLCB3aWR0aCA9IDIpCiAgKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56X2FwcHJveDEsCiAgICB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLAogICAgbmFtZSA9ICJBcHByb3ggMSIsCiAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiLCB3aWR0aCA9IDIpCiAgKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56X2FwcHJveDIsCiAgICB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLAogICAgbmFtZSA9ICJBcHByb3ggMiIsCiAgICBsaW5lID0gbGlzdChjb2xvciA9ICJncmVlbiIsIHdpZHRoID0gMikKICApICU+JQogIGxheW91dCgKICAgIHNjZW5lID0gbGlzdCgKICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIngiLCByYW5nZSA9IHhfcmFuZ2UpLAogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAieSIsIHJhbmdlID0geV9yYW5nZSksCiAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJWYWx1ZSIsIHJhbmdlID0gel9yYW5nZSkKICAgICksCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoCiAgICAgIGxpc3QoCiAgICAgICAgdHlwZSA9ICJidXR0b25zIiwgc2hvd2FjdGl2ZSA9IEZBTFNFLAogICAgICAgIGJ1dHRvbnMgPSBsaXN0KAogICAgICAgICAgbGlzdChsYWJlbCA9ICJQbGF5IiwgbWV0aG9kID0gImFuaW1hdGUiLAogICAgICAgICAgICAgICBhcmdzID0gbGlzdChOVUxMLCBsaXN0KGZyYW1lID0gbGlzdChkdXJhdGlvbiA9IDEwMCwgcmVkcmF3ID0gVFJVRSksIGZyb21jdXJyZW50ID0gVFJVRSkpKSwKICAgICAgICAgIGxpc3QobGFiZWwgPSAiUGF1c2UiLCBtZXRob2QgPSAiYW5pbWF0ZSIsCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KE5VTEwsIGxpc3QobW9kZSA9ICJpbW1lZGlhdGUiLCBmcmFtZSA9IGxpc3QoZHVyYXRpb24gPSAwKSwgcmVkcmF3ID0gRkFMU0UpKSkKICAgICAgICApCiAgICAgICkKICAgICksCiAgICB0aXRsZSA9ICJUaW1lOiAwIgogICkKCiMgQ29udmVydCB0byBwbG90bHkgb2JqZWN0IHdpdGggZnJhbWUgaW5mbwpwYiA8LSBwbG90bHlfYnVpbGQocCkKCiMgSW5qZWN0IGN1c3RvbSB0aXRsZXMgaW50byBlYWNoIGZyYW1lCmZvciAoaSBpbiBzZXFfYWxvbmcocGIkeCRmcmFtZXMpKSB7CiAgdCA8LSB0aW1lX3NlcVtpXQogIGVyciA8LSBzaWduaWYobWF4X2Vycm9yX2F0X2VhY2hfdGltZTJbaV0sIDQpCiAgcGIkeCRmcmFtZXNbW2ldXSRsYXlvdXQgPC0gbGlzdCh0aXRsZSA9IHBhc3RlMCgiVGltZTogIiwgdCwgIiB8IE1heCBFcnJvcjogIiwgZXJyKSkKfQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodCA9IDQsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuY2FwID0gY2FwdGlvbmVyKCJDYXB0aW9uIil9CmZpZyAgIyBEaXNwbGF5IHRoZSBwbG90CmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gOCwgb3V0LndpZHRoID0gIjEwMCUiLCBmaWcuY2FwID0gY2FwdGlvbmVyKCJDYXB0aW9uIil9CnBiCmBgYAoK